package com.colin.util;

import cn.hutool.db.meta.JdbcType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
import com.intellij.openapi.project.ProjectManager;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.colin.database.Config.*;

/**
 * 代码生成工具类
 */
public class GenUtil {

    public static void gen(List<String> tables, String author, String module) {
        try {
            /***
             * 数据库信息配置
             */
            DataSourceConfig dataSourceConfig = configDataSource();
            /**
             * 生成工具类
             **/
            AutoGenerator generator = new AutoGenerator(dataSourceConfig);
            /**
             * 全局变量配置
             */
            generator.global(configGlobel(author));
            /**
             * 设置生成文件包名地址
             */
            generator.packageInfo(configPackage(module));

            /**
             * 生成文件的策略配置
             */
            generator.strategy(configStrategy(tables.toArray(new String[0])));

            /**
             * 生成的类的模板配置
             */
            generator.template(configTemplate());

            /**
             * 自定义实体信息
             **/
//        generator.injection(initInjectionConfig());

            /**
             * 自定义模板解析器
             */
            YaoVelocityTemplateEngine yaoVelocityTemplateEngine = new YaoVelocityTemplateEngine();

            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            Thread.currentThread().setContextClassLoader(yaoVelocityTemplateEngine.getClass().getClassLoader());
            generator.execute(yaoVelocityTemplateEngine);
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 配置基础转换器
     *
     * @return
     */
    private static DataSourceConfig configDataSource() {
        /**数据库链接配置**/
        return new DataSourceConfig.Builder(
                JDBC_URL_MAN,
                JDBC_UserName,
                JDBC_Password
        )
                .dbQuery(new MySqlQuery())
                /**自定义转换器，将tinyint 转换为Integer**/
                .typeConvert(new EasyMySqlTypeConvert())
                .keyWordsHandler(new MySqlKeyWordsHandler())
                .build();
    }

    /**
     * 自定义转换器转换器 内部类
     * 目的将数据库表中定义的  tinyint 或 bit类型转变为 java Integer 类型
     *
     * @author timerbin
     */
    static class EasyMySqlTypeConvert extends MySqlTypeConvert {
        @Override
        public IColumnType processTypeConvert(GlobalConfig config, String fieldType) {
            IColumnType iColumnType = super.processTypeConvert(config, fieldType);
            if (JdbcType.TINYINT.equals(fieldType) || JdbcType.SMALLINT.equals(fieldType)) {
                iColumnType = DbColumnType.INTEGER;
            }
            if (fieldType.equals("bit(1)")) {
                iColumnType = DbColumnType.BYTE;
            }
            if (JdbcType.BIGINT.equals(fieldType)) {
                iColumnType = DbColumnType.LONG;
            }
            if (JdbcType.DATE.equals(fieldType)) {
                iColumnType = DbColumnType.LOCAL_DATE_TIME;
            }
            return iColumnType;
        }
    }

    /**
     * 设置全局变量
     *
     * @return
     */
    private static GlobalConfig configGlobel(String author) {

        GlobalConfig globalConfig = new GlobalConfig.Builder()
                .fileOverride()
                .disableOpenDir()
                /**存放生成文件的文件夹地址 **/
                .outputDir(FILE_STORAGE_FILE_ROOT_PATH)
                .author(author)
                .dateType(DateType.TIME_PACK)
                .commentDate("yyyy-MM-dd hh:mm:ss")
                .build();
        return globalConfig;
    }

    /**
     * 生成文件存储目录配置
     *
     * @return
     */
    private static PackageConfig configPackage(String module) throws Exception {
        String projectPath = FILE_STORAGE_FILE_ROOT_PATH;
        String basePath = ProjectManager.getInstance().getOpenProjects()[0].getBasePath();
        MavenXpp3Reader reader = new MavenXpp3Reader();
        Model model = reader.read(new FileReader(basePath + "/pom.xml"));
        List<String> modules = model.getModules();
        String modulePath = modules.stream().filter(e -> e.contains(module)).findFirst().orElseThrow(() -> new RuntimeException("模块路径错误"));
        String mavenPath = File.separator + modulePath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
        String srcPath = projectPath + mavenPath;
        /**
         * packageInfo配置controller、service、serviceImpl、entity、mapper等文件的包路径
         * 这里包路径可以根据实际情况灵活配置
         */
        String groupId = model.getDependencyManagement().getDependencies().stream().filter(e -> e.getArtifactId().equals(module)).findFirst().orElseThrow(() -> new RuntimeException("模块包名获取错误")).getGroupId();
        Map<String, String> packageInfo = new HashMap<>();
        packageInfo.put(ConstVal.ENTITY, groupId + ".model.entity");
        packageInfo.put(ConstVal.SERVICE, groupId + ".dao");
        packageInfo.put(ConstVal.MAPPER, groupId + ".mapper");

        /**
         * pathInfo配置controller、service、serviceImpl、entity、mapper、mapper.xml等文件的生成路径
         * srcPath也可以更具实际情况灵活配置
         * 后面部分的路径是和上面packageInfo包路径对应的源码文件夹路径
         * 这里你可以选择注释其中某些路径，可忽略生成该类型的文件，例如:注释掉下面pathInfo中Controller的路径，就不会生成Controller文件
         */
        Map<OutputFile, String> pathInfo = new HashMap<>();
        pathInfo.put(OutputFile.service, srcPath + packageInfo.get(ConstVal.SERVICE).replaceAll(File.separator + ".", StringPool.BACK_SLASH + File.separator));
        pathInfo.put(OutputFile.entity, srcPath + packageInfo.get(ConstVal.ENTITY).replaceAll(File.separator + ".", StringPool.BACK_SLASH + File.separator));
        pathInfo.put(OutputFile.mapper, srcPath + packageInfo.get(ConstVal.MAPPER).replaceAll(File.separator + ".", StringPool.BACK_SLASH + File.separator));
        PackageConfig packageConfig = new PackageConfig.Builder()
                /**存放生成文件的 父级 package 地址**/
                .parent("")
                /**存放生成文件的 service 接口 存放的package地址**/
                .service(packageInfo.get(ConstVal.SERVICE))
                /**存放生成文件的 实体类 存放的package地址**/
                .entity(packageInfo.get(ConstVal.ENTITY))
                /**存放生成文件的 mapper 操作类 存放的package地址**/
                .mapper(packageInfo.get(ConstVal.MAPPER))
                /**存放生成文件的 其他实体类 存放的package地址**/
                .pathInfo(pathInfo)
                .build();
        return packageConfig;
    }

    /**
     * 生成文件的策略配置
     *
     * @return
     */
    private static StrategyConfig configStrategy(String[] tables) {
        return new StrategyConfig.Builder()
                .addInclude(tables)
                .entityBuilder()//开始定制实体
                .enableLombok()
                .disableSerialVersionUID()
                .naming(NamingStrategy.underline_to_camel)
                .columnNaming(NamingStrategy.underline_to_camel)
                .logicDeleteColumnName("is_deleted")
                .logicDeletePropertyName("deleted")
                .idType(IdType.AUTO)

                .serviceBuilder()
                .superServiceClass(ServiceImpl.class)
                .formatServiceFileName("%sRepository")

                .mapperBuilder()//开始定制映射器
                .enableMapperAnnotation()
                .superClass(BaseMapper.class)
                .enableBaseColumnList()
                .enableBaseResultMap()
                .formatMapperFileName("%sMapper")
                .build();
    }

    /**
     * 设置其他模板
     *
     * @return
     */
    private static TemplateConfig configTemplate() {
        return new TemplateConfig.Builder()
                .mapper("/templates/mapper.java.vm")
                .service("/templates/service.java.vm")
                .disable(TemplateType.CONTROLLER)
                .disable(TemplateType.SERVICEIMPL)
                .disable(TemplateType.XML)
                .build();
    }

    /**
     * @return
     */
    private static InjectionConfig initInjectionConfig() {
        /**自定义生成模板参数**/
        Map<String, Object> paramMap = new HashMap<>();

        /** 自定义 生成类**/
        Map<String, String> customFileMap = new HashMap<>();
        /**Vo实体**/
        customFileMap.put("vo" + File.separator + "%sVO.java", "/templates/VO.java.vm");
        /**DTO实体**/
        customFileMap.put("dto" + File.separator + "%sDTO.java", "/templates/DTO.java.vm");

        return new InjectionConfig.Builder()
                .customMap(paramMap)
                .customFile(customFileMap)
                .build();
    }


}
